前言
最近準備要做個小小的 side project
快要做到前端的時候想到自己其實還不會用目前 Rails 生態系推薦的 Hotwire 系列工具,因此這兩週就花了些時間研究了一下(如果要學的話推薦學習資源放在最下面)
以下記錄一下重點跟使用心得
Hotwire
Hotwire 指的是 HTML over the wire
,與傳統 JavaScript 透過 API 獲取 JSON 資料並處理的方式不同,Hotwire 是直接通過 API 獲取 HTML,然後將其渲染在頁面上。
它包含三個主要框架:
- Turbo: 負責處理頁面導航和更新,透過伺服器傳送 HTML 來動態更新頁面,並加速連結和表單提交,只更新需要的部分,避免整頁重新載入。
- Stimulus: 用於處理需要用戶端互動的場景,是一個輕量級的 JavaScript 框架。
- Strada: 用於將網頁互動升級到原生應用程式。
在 Rails 中,他認為大部分情境下開發者會使用 Turbo,真的需要寫 Javascript 的時候寫 Stimulus JS,要寫手機作業系統的時候才會用到 strada。
這篇文章主要在研究 Turbo 的使用方式。
Turbo
Turbo 又分成三個部分
- Turbo Drive: 是 Turbo 的核心,它透過攔截連結點擊和表單提交,在背景執行要做的事情來更新頁面,無需整頁重載
- Turbo Frame: 把頁面拆成多個部分,點擊連結或者送出表單之後,只有特定區域的地方會做更新
- Turbo Streams: 透過 WebSocket、SSE(Server-Sent Events) 或表單提交傳遞 HTML 片段來更新頁面
Turbo Drive
Turbo Drive 前一代的祖先是 Turbolink,但那時候只有處理連結,現在 Turbo Drive 額外支援表單的請求
Turbo Drive 做了幾件事情:
- 防止瀏覽器跟隨連結
- 使用歷史紀錄 API 更改瀏覽器 URL
- 使用 fetch 請求獲取新頁面
- 通過替換當前
<body>
元素和合併<head>
元素的內容來渲染回應的 HTML
當只替換
內容時, 標籤內的內容通常不會變動,因此不需要重新下載字體、CSS、JS 等文件,這能加快頁面渲染速度。然而,當 JS 或 CSS 發生變動時,我們希望能重新載入整個頁面。Turbo Drive 會在每次新請求時,對比當前頁面與回應頁面中
內標記為 data-turbo-track=“reload” 的 DOM 元素,若發現差異就會重新載入頁面。所以我們可以看到目前在 Rails layout 中,css / js 都加上了這個屬性
1 | <%# app/views/layouts/application.html.erb %> |
Turbo Frame
它透過替換具有相同 ID 的 <turbo-frame>
標籤內容來更新頁面。
已下面的範例程式碼來說
_task.html.erb 完全被 turbo frame tag 包住,所以如果點擊 Edit
按鈕會全部被替換掉,而替換的內容就是 edit.html.erb 頁面中有著同樣 id 的 turbo frame tag 的部分
範例程式碼
1 | %% index.html.erb %% |
1 | %% _task.html.erb %% |
1 | %% edit.html.erb %% |
另外若要替換整個頁面,可以使用 data-turbo-frame="_top"
屬性。
通常點擊連結之後,會跟包覆著連結的 turbo frame tag 互動,但也可以指定不是包覆這連結的 frame,這時候可以用 data: { turbo_frame: ...}
這個屬性
範例程式碼
1 | <main class="container"> |
Turbo Stream
Turbo Stream 可以將 HTML 片段發送到頁面上,替換或修改現有內容。它專門用於在單次請求中更新頁面多個部分的情況。
跟 turbo frame 的差異有幾點
- Turbo frame 一次只能替換已經存在的 frame,不能做 append / prepend 的動作
- Turbo stream 還可以用 websocket 來更新頁面(當然也可以用 POST request 來更新)
turbo stream 的核心也是靠 HTML snippet 來實現,但跟 turbo frame 不同,turbo stream tag 中一定包含一個 action,所有的 action 都需要一個 target 來指定對象(只有 refresh 這個 action 不需要)
這是 turbo stream tag 在 HTML 中的長相:
1 | <turbo-stream action="action_to_take" target="element_to_update"> |
這裡的實現比較複雜一些,推薦要看使用範例的話直接看 Turbo Rails Tutorial 裡面有滿多的範例
心得
總算是大致上理解了這套系統如何使用,但在好不容易理解完後,我應該只會在小專案嘗試使用 Turbo
因為在複雜專案中,前端效果經常是巢狀結構,而要使用 Turbo 處理這種巢狀效果的話想向上維護起來有點困難
此外,複雜專案中常需重複使用同樣的元件,因此以 component-based 的前端框架會更容易維護
References
Hotwire Crash-Course
Turbo Rails Tutorial
卡米哥 demo
Turbo 官方文件